/*
Copyright (c) 2006, Bruce Weirdan
All rights reserved.

Redistribution and use in source and binary forms,
with or without modification, are permitted provided
that the following conditions are met:

    * Redistributions of source code must retain the
above copyright notice, this list of conditions and
the following disclaimer.
    * Redistributions in binary form must reproduce
the above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
    * The name of the copyright holder may NOT be used
to endorse or promote products derived from this
software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/

// ==UserScript==
// @name   MarkSmilies
// @namespace   tag:weirdan@gmail.com,2005:greasemonkey#marksmilies
// @description   Provides the access to Pimp' collection of smilies from PHPDN forums post form
// ==/UserScript==

var Config = {};
Config.repository = 'http://pa.ranoid.co.uk/play/smilies/smilies.php';

function Repository(url, baseUrl) {
    var me = this;
    // public
    me.url = url;
    me.baseUrl = baseUrl || url.replace(/\/[^\/]*$/, '/');

    me.onload = function() {};

    // private
    var hrefs = [];
    var status = Repository.IDLE;

    me.load = function(handler) {/*{{{*/

        GM_log('Load started...');

        status = Repository.LOADING;
        hrefs = []; // empty the hrefs array

        me.onload = handler;

        GM_xmlhttpRequest({
            method: 'GET',
            url: me.url,
            onload: function(details) {

                GM_log('Loaded!');

                // parse response text into dom tree
                var dom = document.createElement('div');
                dom.innerHTML = '<base href="' + me.baseUrl + '" />' + details.responseText;
               
                GM_log('Parsed!');

                // get all images
                var urls = dom.getElementsByTagName('img');

                for(var i=0, l=urls.length; i<l; i++) {
                    hrefs.push(urls[i].src);       
                }

                status = Repository.COMPLETE;
                GM_log('Loading complete. Calling handler...');
                me.onload(hrefs);   
            }
        });     
    };/*}}}*/

    me.updateStore = function(store) {/*{{{*/
        GM_log('Starting update process...');
        me.load(function(hrefs) {
            if(hrefs.length) {
                // build config array
                store.selectNew(hrefs).forEach(function(href) {
                    GM_log('Found new smilie (' + href + '), adding...');
                    store.addUrl(href, true);
                });
                store.save();
                alert('Smilies collection successfuly updated from ' + me.url +'.\nYou may need to reconfigure your smilie list');
            }
        });   
    };/*}}}*/
}

Repository.IDLE = 0;
Repository.LOADING = 1;
Repository.COMPLETE = 2;

function LocalStore(storeName) {
    var me = this;
    me.storeName = storeName || 'MarkSmilies.activeSmilies';

    var smilies = {};
    var count = 0;

    smilies.forEach = function(callback) {/*{{{*/
        for(i in smilies) {
            if( !smilies.exists(i) ) continue;
            callback(smilies[i], i);
        }
    };/*}}}*/

    smilies.exists = function(url) {/*{{{*/
        return (typeof smilies[url] != 'undefined' && typeof smilies[url] != 'function');
    };/*}}}*/

    me.load = function() {/*{{{*/
        var newSmilies = deserialize(GM_getValue(me.storeName));   
        count = newSmilies.count;
        newSmilies = newSmilies.urls;
        newSmilies.forEach = smilies.forEach;
        newSmilies.exists = smilies.exists;
        smilies = newSmilies;
    };/*}}}*/

    me.save = function() {/*{{{*/
        GM_setValue(me.storeName, serialize(smilies));
    };/*}}}*/

    me.selectNew = function(urls) {/*{{{*/
        var ret = [];
        urls.forEach(function(url) {
            if(!smilies.exists(url))
                ret.push(url);
        });
        return ret;
    };/*}}}*/
   
    me.addUrl = function(url, enabled) {/*{{{*/
        smilies[url] = enabled;       
        count++;
    };/*}}}*/
   
    me.getEnabled = function() {/*{{{*/
        var ret = [];
        smilies.forEach(function(enabled, url) {
            if(enabled) ret.push(url);
        });
        return ret;
    };/*}}}*/

    me.getAll = function() {/*{{{*/
        var ret = [];
        smilies.forEach(function(enabled, url) {
            ret.push(url);
        });
        return ret;
    };/*}}}*/

    var serialize = function(arr) {/*{{{*/
        var ret = '';   
        smilies.forEach(function(enabled, url) {
            if(ret.length) ret += '|';
            ret += url + ';' + (enabled ? '1' : '0');
        });
        return ret;
    };/*}}}*/

    var deserialize = function(str) {/*{{{*/
        GM_log('Deserializing string: ' + str);
        var ret = {'urls':{}, 'count':0};
        if(!str) return ret;
        var elts = str.split('|');     
        elts.forEach(function(elt) {
            var parts = elt.split(';');
            parts[1] = (parseInt(parts[1]) ? true : false);
            ret.urls[parts[0]] = parts[1];
            ret.count++;
        });
        return ret;
    };/*}}}*/

    me.dump = function() {/*{{{*/
        var str = '';
        smilies.forEach(function(enabled, url) {
            if(str.length) str += '\n';
            str += url + ' = ' + (enabled ? 'true' : 'false');
        });
        GM_log(str);
    };/*}}}*/
   
    me.clear = function() {/*{{{*/
        var newSmilies = {};       
        newSmilies.forEach = smilies.forEach;
        newSmilies.exists = smilies.exists;
        smilies = newSmilies;
    };/*}}}*/

    me.enable = function(url) {/*{{{*/
        if(smilies.exists(url))
            smilies[url] = true;
    };/*}}}*/

    me.disable = function(url) {/*{{{*/
        if(smilies.exists(url))
            smilies[url] = false;
    };/*}}}*/

    me.isEnabled = function(url) {/*{{{*/
        if(smilies.exists(url) && smilies[url])
            return true;
        return false;
    };/*}}}*/
   
    me.getCount = function() {/*{{{*/
        return count;
    };/*}}}*/

    me.load();
}

var Sites = {};

// ================= Plugins should be placed here ========================

Sites.getSite = function() {/*{{{*/
    GM_log('Searching for the proper site...');
    for(i in Sites) {
        if(typeof Sites[i] != 'object' || typeof(Sites[i].isThisSite) != 'function') continue;
        GM_log('Processing site: ' + i + ', result = ' + Sites[i].isThisSite());
        if(Sites[i].isThisSite())
            return Sites[i];
    }
    GM_log('Found no supported sites, returning dummy site');
    return {
        initUI : function() {
            GM_log('dummy site, define your own');
        },

        isThisSite : function() {
            return true;
        }
    };
};/*}}}*/

var store = new LocalStore();

if(!store.getCount()) // invoked for the first time, proceed with smilie list update
    new Repository(Config.repository).updateStore(store);
   
window.addEventListener('load', function() { 
	Sites.getSite().initUI(store); // init
}, false); 

//----------- Menu Commands ------------
GM_registerMenuCommand('[MS] Update smilies', function() {
    new Repository(Config.repository).updateStore(store);
});

GM_registerMenuCommand('[MS] Clear smilies', store.clear);

GM_registerMenuCommand('[MS] Dump smilies', store.dump);

GM_registerMenuCommand('[MS] Configure smilies', function() {/*{{{*/
       
    var popup = document.createElement('div');
    popup.style.position = 'absolute';
    popup.style.background = 'white';
    popup.style.border = 'silver solid 2px';

    var popupHeader = document.createElement('div');
    popupHeader.style.background = 'silver';
    popupHeader.style.color = 'white';
    popupHeader.style.fontWeight = 'bold';
    popupHeader.appendChild(document.createTextNode('MarkSmilies configuration'));

    var popupOkButton = document.createElement('input');
    popupOkButton.className = 'button';
    popupOkButton.type = 'button';
    popupOkButton.style.marginLeft = 'auto';
    popupOkButton.style.marginRight = 'auto';
    popupOkButton.style.width = '15em';
    popupOkButton.style.display = 'block';
    popupOkButton.value = 'Save configuration';

    popupOkButton.addEventListener('click', function(e) {
        store.save();
        popup.parentNode.removeChild(popup);
    }, false);

    popup.appendChild(popupHeader);

    store.getAll().forEach(function(url) {
        var img = document.createElement('img');
        img.src = url;
        img.style.border = 'solid 2px ' + (store.isEnabled(url) ? 'green' : 'red');
        img.addEventListener('click', function(e) {
            var enabled = store.isEnabled(url);   
            img.style.borderColor = (enabled ? 'red red red red' : 'green green green green');
            if(enabled)
                store.disable(url);
            else
                store.enable(url);
        }, false);
        popup.appendChild(img);
    });
    popup.appendChild(document.createElement('br'));
    popup.appendChild(popupOkButton);

    document.body.appendChild(popup);

    // center configuration popup
    popup.style.left = (document.body.clientWidth - popup.offsetWidth) / 2;
    popup.style.top = 400 + (document.body.clientHeight - popup.offsetHeight) /2;
});/*}}}*/
